#include "Components/ComponentLevel.h"
#include "Components/ComponentPlayer.h"
#include "Components/ComponentVitalStats.h"
#include "Components/ComponentSickness.h"
#include "Components/ComponentFlu.h"
#include "Components/ComponentClothing.h"

#include "Network/PackageManager.h"
#include "Network/Packages/ComponentPlayerPackage.h"
#include "Player/PlayerData.h"

#include "Subsystems/SubsystemGameInfo.h"
#include "Terrain/Terrain.h"
#include "Managers/BlocksManager.h"
#include "Blocks/ClothingBlock.h"

#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

#include <cfloat>

namespace PocketSurvival
{

    float ComponentLevel::getStrengthFactor()
    {
        return strengthFactor;
    }
    float ComponentLevel::getResilienceFactor()
    {
        return resilienceFactor;
    }
    float ComponentLevel::getSpeedFactor()
    {
        return speedFactor;
    }
    float ComponentLevel::getHungerFactor()
    {
        return hungerFactor;
    }

    void ComponentLevel::addExperience(int32_t count, bool playSound)
    {
		netAddExperience(count, playSound);
        GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentPlayerPackage>(componentPlayer, count, playSound, componentPlayer->playerData->level));
    }

	void ComponentLevel::netAddExperience(int32_t count, bool playSound)
	{
		for (int32_t i = 0; i < count; i++)
		{
			float num = 0.012f / MathUtils::Pow(1.08f, MathUtils::Floor(componentPlayer->playerData->level - 1.0f));
			componentPlayer->playerData->level += num;
		}
	}

    float ComponentLevel::calculateStrengthFactor()
    {
		float num = (componentPlayer->playerData->playerClass == PlayerClass::Female) ? 0.8f : 1.0f;
		float num2 = 1.0f * num;
		float level = componentPlayer->playerData->level;
		float num3 = 1.0f + 0.05f * MathUtils::Floor(MathUtils::Clamp(level, 1.0f, 21.0f) - 1.0f);
		float num4 = num2 * num3;
		float stamina = componentPlayer->componentVitalStats->stamina;
		float num5 = MathUtils::Lerp(0.5f, 1.0f, MathUtils::Saturate(4.0f * stamina)) * MathUtils::Lerp(0.9f, 1.0f, MathUtils::Saturate(stamina));
		float num6 = num4 * num5;
		float num7 = componentPlayer->componentSickness->isSick() ? 0.75f : 1.0f;
		float num8 = num6 * num7;
		float num10 = num8;
		float num11 = componentPlayer->componentFlu->hasFlu() ? 0.75f : 1.0f;
		float num12 = num10 * num11;
		float num13 = (componentPlayer->componentFlu->isCoughing() == false) ? 1 : 0;
		float num14 = num12 * num13;
        float num15 = (GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>()->gameMode == GameMode::Harmless || componentPlayer->playerData->playerGameMode == PlayerGameMode::Harmless) ? 1.25f : 1.0f;
		float result = num14 * num15;
		return result;
    }

    float ComponentLevel::calculateResilienceFactor()
    {
        SubsystemGameInfo *gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
		float num = (componentPlayer->playerData->playerClass == PlayerClass::Female) ? 0.8f : 1.0f;
		float num2 = 1.0f * num;
		float level = componentPlayer->playerData->level;
		float num3 = 1.0f + 0.05f * MathUtils::Floor(MathUtils::Clamp(level, 1.0f, 21.0f) - 1.0f);
		float num4 = num2 * num3;
		float num5 = componentPlayer->componentSickness->isSick() ? 0.75f : 1.0f;
		float num6 = num4 * num5;
		float num7 = componentPlayer->componentFlu->hasFlu() ? 0.75f : 1.0f;
		float num8 = num6 * num7;
		float num9 = 1.0f;
		if (gameInfo->gameMode == GameMode::Harmless)
		{
			num9 = 1.5f;
		}
        if (gameInfo->gameMode == GameMode::Survival)
        {
            num9 = 1.25f;
        }
        if (gameInfo->gameMode == GameMode::Creative)
		{
			num9 = FLT_MAX;
		}
		float result = num8 * num9;
		return result;
    }

    float ComponentLevel::calculateSpeedFactor()
    {
		float num = 1.0f;
		float num2 = (componentPlayer->playerData->playerClass == PlayerClass::Female) ? 1.03f : 1.0f;
		num *= num2;
		float level = componentPlayer->playerData->level;
		float num3 = 1.0f + 0.02f * MathUtils::Floor(MathUtils::Clamp(level, 1.0f, 21.0f) - 1.0f);
		num *= num3;
		float clothingFactor = 1.0f;
        for(int32_t clothe : componentPlayer->componentClothing->getClothes(ClothingSlot::Head))
        {
            AddClothingFactor(clothe, clothingFactor);
        }
        for(int32_t clothe : componentPlayer->componentClothing->getClothes(ClothingSlot::Torso))
        {
            AddClothingFactor(clothe, clothingFactor);
        }
        for(int32_t clothe : componentPlayer->componentClothing->getClothes(ClothingSlot::Legs))
        {
            AddClothingFactor(clothe, clothingFactor);
        }
        for(int32_t clothe : componentPlayer->componentClothing->getClothes(ClothingSlot::Feet))
        {
            AddClothingFactor(clothe, clothingFactor);
        }
		num *= clothingFactor;
		float stamina = componentPlayer->componentVitalStats->stamina;
		float num4 = MathUtils::Lerp(0.5f, 1.0f, MathUtils::Saturate(4.0f * stamina)) * MathUtils::Lerp(0.9f, 1.0f, MathUtils::Saturate(stamina));
		num *= num4;
		float num5 = componentPlayer->componentSickness->isSick() ? 0.75f : 1.0f;
		num *= num5;
		float num7 = componentPlayer->componentFlu->hasFlu() ? 0.75f : 1.0f;
		num *= num7;
		float num8 = (componentPlayer->componentFlu->isCoughing() == false) ? 1.0f : 0.0f;
		num *= num8;
		return num;
    }

    float ComponentLevel::calculateHungerFactor()
    {
        SubsystemGameInfo *gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
		float num = (componentPlayer->playerData->playerClass == PlayerClass::Female) ? 0.7f : 1.0f;
		float num2 = 1.0f * num;
		float level = componentPlayer->playerData->level;
		float num3 = 1.0f - 0.01f * MathUtils::Floor(MathUtils::Clamp(level, 1.0f, 21.0f) - 1.0f);
		float num4 = num2 * num3;
		float num5 = 1.0f;
		if (gameInfo->gameMode == GameMode::Harmless)
		{
			num5 = 0.66f;
		}
        if (gameInfo->gameMode == GameMode::Survival)
        {
            num5 = 0.75f;
        }
        if (gameInfo->gameMode == GameMode::Creative)
		{
			num5 = 0.0f;
		}
		float result = num4 * num5;
		return result;
    }

    void ComponentLevel::AddClothingFactor(int32_t clothingValue, float &clothingFactor)
    {
        ClothingBlock *clothingBlock = GameSingleton::blocksManager->blocks[Terrain::ExtractContents(clothingValue)]->asBlock<ClothingBlock*>();
        if(clothingBlock != nullptr)
        {
            const ClothingData &clothingData = clothingBlock->getClothingData(Terrain::ExtractData(clothingValue));
            if (clothingData.MovementSpeedFactor != 1.0f)
            {
                clothingFactor *= clothingData.MovementSpeedFactor;
            }
        }
    }

    void ComponentLevel::update(float dt)
    {
        strengthFactor = calculateStrengthFactor();
        resilienceFactor = calculateResilienceFactor();
        speedFactor = calculateSpeedFactor();
        hungerFactor = calculateHungerFactor();
    }

    static const std::string componentName = "Level";
    const std::string &ComponentLevel::getName() const
    {
        return componentName;
    }
    const std::string &ComponentLevel::GetName()
    {
        return componentName;
    }
    void ComponentLevel::load(const nlohmann::json &jsonData)
    {
        strengthFactor = 1.0f;
        resilienceFactor = 1.0f;
        speedFactor = 1.0f;
        hungerFactor = 1.0f;
        componentPlayer = GameSingleton::gameRegistry->get<ComponentPlayer*>(entity);
    }
    void ComponentLevel::save(nlohmann::json &jsonData) {}
    void ComponentLevel::saveToMsgJson(nlohmann::json &jsonData) {}

} // namespace PocketSurvival
